import base64
import logging
import random
import uuid
from io import BytesIO
from typing import Optional

import redis.asyncio as redis
from PIL import Image, ImageDraw, ImageFont

from app.schemas.auth import Captcha

logger = logging.getLogger(__name__)


class CaptchaUtils:
    """
    验证码工具类

    提供验证码生成和验证功能，支持算术验证码和字母数字验证码
    """

    def __init__(
        self,
        redis: redis.Redis,
        captcha_type: str = "arithmetic",
        timeout: int = 300,
        font_path: Optional[str] = None,
        interference: int = 5,
        key_prefix: Optional[str] = None,
    ):
        """
        初始化验证码工具

        参数:
            captcha_type: 验证码类型，可选 "arithmetic"（算术）或 "alphanumeric"（字母数字）
            timeout: 验证码过期时间（秒）
            font_path: 字体文件路径，如果为 None 则使用默认字体
            interference: 干扰线数量
            redis: Redis 实例，用于存储验证码
            key_prefix: 验证码键的前缀，用于避免键冲突，默认 None
        """
        self.captcha_type = captcha_type
        self.timeout = timeout
        self.font_path = font_path
        self.interference = interference
        self.redis = redis
        self.key_prefix = key_prefix or "captcha"

    async def generate_captcha(self) -> Captcha:
        """
        生成验证码图片和对应的验证码键

        返回:
            Captcha: 包含 captcha_key 和 captcha_base64 的验证码响应对象
        """
        # 初始化答案变量
        answer = ""

        # 根据验证码类型生成表达式和答案
        if self.captcha_type == "arithmetic":
            operators = ["+", "-", "*"]
            operator = random.choice(operators)

            a = random.randint(1, 20)
            b = random.randint(1, 20)
            expression = f"{a}{operator}{b}="

            if operator == "+":
                answer = str(a + b)
            elif operator == "-":
                # 确保结果为正数
                a, b = max(a, b), min(a, b)
                expression = f"{a}{operator}{b}="
                answer = str(a - b)
            elif operator == "*":
                # 限制乘法结果不要太大
                a = random.randint(1, 9)
                b = random.randint(1, 9)
                expression = f"{a}{operator}{b}="
                answer = str(a * b)
        else:
            # 字母数字验证码，生成4位随机字符（不区分大小写）
            chars = "abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ23456789"  # 去掉了容易混淆的字符
            expression = "".join(random.choices(chars, k=4))
            answer = expression.lower()

        # 创建验证码图片
        width, height = 120, 40
        image = Image.new("RGB", (width, height), (255, 255, 255))
        draw = ImageDraw.Draw(image)

        # 尝试加载字体
        try:
            if self.font_path:
                font = ImageFont.truetype(self.font_path, 24)
            else:
                font = ImageFont.load_default()
        except (OSError, IOError):
            font = ImageFont.load_default()

        # 计算文字位置
        bbox = draw.textbbox((0, 0), expression, font=font)
        text_width = bbox[2] - bbox[0]
        text_height = bbox[3] - bbox[1]
        text_x = (width - text_width) / 2
        text_y = (height - text_height) / 2

        # 生成随机文本颜色
        text_color = (random.randint(0, 100), random.randint(0, 100), random.randint(0, 100))
        draw.text((text_x, text_y), expression, font=font, fill=text_color)

        # 添加干扰线
        for _ in range(self.interference):
            start = (random.randint(0, width), random.randint(0, height))
            end = (random.randint(0, width), random.randint(0, height))
            line_color = (random.randint(150, 255), random.randint(150, 255), random.randint(150, 255))
            draw.line([start, end], fill=line_color, width=1)

        # 添加干扰点
        for _ in range(self.interference * 20):
            point = (random.randint(0, width), random.randint(0, height))
            point_color = (random.randint(100, 200), random.randint(100, 200), random.randint(100, 200))
            draw.point(point, fill=point_color)

        # 将图片转为 Base64
        buffered = BytesIO()
        image.save(buffered, format="PNG")
        img_base64 = base64.b64encode(buffered.getvalue()).decode("utf-8")
        captcha_base64 = f"data:image/png;base64,{img_base64}"

        # 生成验证码键并存储答案
        captcha_key = uuid.uuid4().hex

        # 使用Redis存储验证码，设置过期时间
        await self.redis.setex(f"{self.key_prefix}:{captcha_key}", self.timeout, answer)

        # if settings.DEBUG:
        #     logger.debug(f"DEBUG: captcha_key={captcha_key}, answer={answer}")
        #     image.save(f"data/captcha_{captcha_key}_{answer}.png")

        return Captcha(captcha_key=captcha_key, captcha_base64=captcha_base64)

    async def verify_captcha(self, captcha_key: str, user_input: str) -> bool:
        """
        验证验证码是否正确

        参数:
            captcha_key: 验证码键
            user_input: 用户输入的验证码

        返回:
            bool: 验证是否成功
        """
        # 从Redis中获取验证码答案
        correct_answer = await self.redis.get(f"{self.key_prefix}:{captcha_key}")
        if not correct_answer:
            return False

        # 由于Redis客户端设置了decode_responses=True，correct_answer已经是字符串类型
        # 不需要再调用decode方法
        # correct_answer: str = correct_answer.decode("utf-8")

        # 验证验证码
        if user_input.lower() == correct_answer.lower():
            # 验证成功后移除验证码
            await self.redis.delete(f"{self.key_prefix}:{captcha_key}")
            return True

        return False


# 创建默认的验证码工具实例（需要提供 Redis 实例）
# captcha_utils = CaptchaUtils(redis=redis_instance)
